package com.atguigu.gulimall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.gulimall.product.entity.vo.CateJsonVo;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.product.dao.CategoryDao;
import com.atguigu.gulimall.product.entity.CategoryEntity;
import com.atguigu.gulimall.product.service.CategoryService;



@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {


    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Cacheable(value = {"category"},key = "#root.methodName",sync = true)
    @Override
    public Map<String, List<CateJsonVo>> getCatalogJson() {
        Map<String, List<CateJsonVo>> cateJsonWithSpringCache = getCateJsonWithSpringCache();
        return cateJsonWithSpringCache;
    }

    @Cacheable(value = {"category"},key = "#root.method.name")
    @Override
    public List<CategoryEntity> getLeveCate() {
        return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid",0));
    }

    public Map<String, List<CateJsonVo>> getCateJsonWithRedisson() {
        //获取锁
        RLock lock = redissonClient.getLock("lock");
        Map<String, List<CateJsonVo>> result=new HashMap<>();
        lock.lock();
        try {
                result= getStringListMap();
            }catch (Exception e){
                log.error("获取锁失败");
            }finally {
                lock.unlock();
            }
            return result;
    }





    @Override
    public Map<String, List<CateJsonVo>> getCateJson() {
        return getCateJsonWithRedisson();

    }

    public Map<String, List<CateJsonVo>> getCateJsonWithLock() {
        //获取锁
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,2,TimeUnit.SECONDS);
        if (lock){
            Map<String, List<CateJsonVo>> result=new HashMap<>();
            try {
                //查询缓存
                result= getStringListMap();
            }catch (Exception e){
                //删除锁
                String s="if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                Integer lock1 = redisTemplate.execute(new DefaultRedisScript<>(s, Integer.class), Arrays.asList("lock"), uuid);
            }
            return result;
        }else {
            //自旋等待
            return   getCateJsonWithLock();
        }

    }

    private Map<String, List<CateJsonVo>> getStringListMap() {
        String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isNotEmpty(catalogJSON)){
            Map<String,List<CateJsonVo>> result=JSON.parseObject(catalogJSON,new TypeReference< Map<String,List<CateJsonVo>>>(){});
            return result;
        }

        System.out.println("查询了数据库");
        //查询全部
        List<CategoryEntity> selectList = baseMapper.selectList(null);
        List<CategoryEntity> leveCate = getParent_cid(selectList,0L);
        Map<String, List<CateJsonVo>> collect = leveCate.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());
            //二级封装
            List<CateJsonVo> catelog2vos = null;
            if (categoryEntities != null) {
                catelog2vos = categoryEntities.stream().map(l2 -> {
                    CateJsonVo cateJson2Vo = new CateJsonVo(v.getCatId(), null, l2.getCatId(), l2.getName());
                    //三级封装
                    List<CategoryEntity> categoryEntities3 = getParent_cid(selectList, l2.getCatId());
                    if (categoryEntities3 != null) {
                        List<CateJsonVo.Catelog3List> catelog3Lists = categoryEntities3.stream().map(l3 -> {
                            CateJsonVo.Catelog3List catelog3Vo = new CateJsonVo.Catelog3List(l2.getCatId(), l3.getCatId(), l3.getName());
                            return catelog3Vo;
                        }).collect(Collectors.toList());

                        cateJson2Vo.setCatelog3List(catelog3Lists);
                    }
                    return cateJson2Vo;
                }).collect(Collectors.toList());
            }

            return catelog2vos;
        }));
        redisTemplate.opsForValue().set("catalogJSON",collect.toString(),1, TimeUnit.DAYS);
        return collect;
    }

    private List<CategoryEntity> getParent_cid(List<CategoryEntity> l2, Long catId) {
        List<CategoryEntity> collect = l2.stream().filter(e -> e.getParentCid().equals(catId)).collect(Collectors.toList());
        return collect;
    }


    public Map<String, List<CateJsonVo>> getCateJsonWithSpringCache() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        List<CategoryEntity> categoryLevel1 = getBypCid(0L, categoryEntities);
        Map<String, List<CateJsonVo>> collect = categoryLevel1.stream().collect(Collectors.toMap(k -> {return k.getCatId().toString();}, v -> {
            // 二级放一级
            List<CategoryEntity> categoryLevel2 = getBypCid(v.getCatId(), categoryEntities);
            List<CateJsonVo> cateJsonVoList = categoryLevel2.stream().map(item -> {
                CateJsonVo cateJsonVo = new CateJsonVo();
                cateJsonVo.setCatelog1Id(item.getParentCid());
                cateJsonVo.setId(item.getCatId());
                cateJsonVo.setName(item.getName());
                // 三级放二级
                List<CategoryEntity> categoryLevel3 = getBypCid(item.getCatId(), categoryEntities);
                List<CateJsonVo.Catelog3List> catelog3Lists = categoryLevel3.stream().map(categoryEntity -> {
                    CateJsonVo.Catelog3List catelog3 = new CateJsonVo.Catelog3List();
                    catelog3.setCatelog2Id(categoryEntity.getParentCid());
                    catelog3.setId(categoryEntity.getCatId());
                    catelog3.setName(categoryEntity.getName());
                    return catelog3;
                }).collect(Collectors.toList());
                cateJsonVo.setCatelog3List(catelog3Lists);
                return cateJsonVo;
            }).collect(Collectors.toList());
            return cateJsonVoList;
        }));
        return collect;
    }


    private List<CategoryEntity> getBypCid(Long pCid, List<CategoryEntity> categoryEntities) {
        List<CategoryEntity> collect = categoryEntities.stream().filter(item -> {
            return item.getParentCid().equals(pCid);
        }).collect(Collectors.toList());
        return collect;
    }

}